package l1j.server.server.utils;

import static l1j.server.server.model.skill.L1SkillId.MEDITATION;
import static l1j.server.server.model.skill.L1SkillId.TELEPORT_DELAY;
import static l1j.server.server.model.skill.L1SkillId.TELEPORT_PROTECTION;
import static l1j.server.server.model.skill.L1SkillId.WIND_SHACKLE;
import static l1j.server.server.model.skill.L1SkillId.PC_IsPinkName;

import java.util.Collection;
import java.util.HashSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import l1j.opqlo.Server.L1World.L1World;
import l1j.opqlo.Server.L1World.WorldClan;
import l1j.server.server.model.L1CastleLocation;
import l1j.server.server.model.L1Clan;
import l1j.server.server.model.L1Location;
import l1j.server.server.model.Instance.L1DollInstance;
import l1j.server.server.model.Instance.L1NpcInstance;
import l1j.server.server.model.Instance.L1PcInstance;
import l1j.server.server.model.Instance.L1PetInstance;
import l1j.server.server.model.Instance.L1SummonInstance;
import l1j.server.server.model.map.L1Map;
import l1j.server.server.model.map.L1WorldMap;
import l1j.server.server.serverpackets.S_ChangeHeading;
import l1j.server.server.serverpackets.S_CharVisualUpdate;
import l1j.server.server.serverpackets.S_DollPack;
import l1j.server.server.serverpackets.S_MapID;
import l1j.server.server.serverpackets.S_OtherCharPacks;
import l1j.server.server.serverpackets.S_OwnCharPack;
import l1j.server.server.serverpackets.S_PetPack;
import l1j.server.server.serverpackets.S_PinkName;
import l1j.server.server.serverpackets.S_SkillIconWindShackle;
import l1j.server.server.serverpackets.S_SummonPack;
import l1j.server.server.serverpackets.S_SystemMessage;

public class Teleportation {

	private static final Logger _log = Logger.getLogger(Teleportation.class.getName());

	/**
	 * 移動後座標更新
	 * 
	 * @param pc
	 */
	public static void actionTeleportation(final L1PcInstance pc) {
		try {
			if (pc == null) {
				return;
			}
			if (pc.getOnlineStatus() == 0) {
				return;
			}
			if (pc.getNetConnection() == null) {
				return;
			}
//			if (pc.isDead()) {
//				return;
//			}
			pc.killSkillEffectTimer(TELEPORT_DELAY);
				// 解除舊座標障礙宣告
				pc.getMap().setPassable(pc.getLocation(), true);

				int x = pc.getTeleportX();
				int y = pc.getTeleportY();
				short mapId = pc.getTeleportMapId();
				final int head = pc.getTeleportHeading();

				final L1Map map = L1WorldMap.getInstance().getMap(mapId);

				if (!pc.isGm()) {
					if (!map.isInMap(x, y)) {
						x = pc.getX();
						y = pc.getY();
						mapId = pc.getMapId();
					} else if (L1CastleLocation.checkInAllfatArea(x, y, mapId)) {// opqlo
						x = pc.getX();
						y = pc.getY();
						mapId = pc.getMapId();
					}
				}
//
//				// TODO 修正角色交易時角色順移可進行交易的BUG
//				if (pc.getTradeID() != 0) {
//					final L1Trade trade = new L1Trade();
//					trade.TradeCancel(pc);
//				}
//				// TODO 修正角色交易時角色順移可進行交易的BUG

				final L1Clan clan = WorldClan.get().getClan(pc.getClanname());
				if (clan != null) {
					if (clan.getWarehouseUsingChar() == pc.getId()) { // 自キャラがクラン倉庫使用中
						clan.setWarehouseUsingChar(0); // クラン倉庫のロックを解除
					}
				}
				// 防止座標異常

				L1World.getInstance().moveVisibleObject(pc, mapId);
				pc.setLocation(x, y, mapId);
				// 新增座標障礙宣告
				pc.getMap().setPassable(pc.getLocation(), false);
				if(pc.TelePortSetSkill){
					if (!pc.getMap().isSafetyZone(x, y)) {
						pc.setSkillEffect(TELEPORT_PROTECTION, 4000);
						pc.sendPackets(new S_SystemMessage("受到傳送結界保護，3秒內無法PVP。"));
					}
					pc.TelePortSetSkill = false;
				}
				pc.setLastLocation(pc.getLocation());
				pc.setHeading(head);
				pc.sendPackets(new S_ChangeHeading(pc));
				pc.killSkillEffectTimer(MEDITATION);

				if (pc.isReserveGhost()) { // 鬼魂狀態解除
					pc.endGhost();
				}
				if (pc.isGhost() || pc.isGmInvis()) {
				} else if (pc.isInvisble()) {
					pc.broadcastPacketForFindInvis(new S_OtherCharPacks(pc, true),true);
				} else {
					pc.broadcastPacketAll(new S_OtherCharPacks(pc));
				}
				pc.sendPackets(new S_MapID(pc.getMapId(), pc.getMap().isUnderwater()));
				// 地圖水中狀態
				pc.sendPackets(new S_OwnCharPack(pc));
				// spr番号6310, 5641の変身中にテレポートするとテレポート後に移動できなくなる
				// 武器を着脱すると移動できるようになるため、S_CharVisualUpdateを送信する

				pc.removeAllKnownObjects();
				pc.sendVisualEffectAtTeleport(); // クラウン、毒、水中等の視覚効果を表示
				pc.updateObject();
				pc.sendPackets(new S_CharVisualUpdate(pc));
				pc.setCallClanId(0); // コールクランを唱えた後に移動すると召喚無効
				/*
				 * subjects ペットとサモンのテレポート先画面内へ居たプレイヤー。
				 * 各ペット毎にUpdateObjectを行う方がコード上ではスマートだが、
				 * ネットワーク負荷が大きくなる為、一旦Setへ格納して最後にまとめてUpdateObjectする。
				 */
				final HashSet<L1PcInstance> subjects = new HashSet<L1PcInstance>();
				subjects.add(pc);

				if (!pc.isGhost()) {
					if (pc.getMap().isTakePets()) {
						// ペットとサモンも一緒に移動させる。
						for (final L1NpcInstance petNpc : pc.getPetList().values()) {
							// テレポート先の設定
							final L1Location loc = pc.getLocation().randomLocation(3, false);
							int nx = loc.getX();
							int ny = loc.getY();
							if (pc.getMapId() == 5125 || pc.getMapId() == 5131
									|| pc.getMapId() == 5132
									|| pc.getMapId() == 5133
									|| pc.getMapId() == 5134) { // ペットマッチ会場
								nx = 32799 + Random.nextInt(5) - 3;
								ny = 32864 + Random.nextInt(5) - 3;
							}
							// opqlo 賭肥區判斷
							if (L1CastleLocation.checkInAllfatArea(nx, ny, mapId)) {
								nx = pc.getX();
								ny = pc.getY();
							}
							// opqlo 賭肥區判斷
							teleport(petNpc, nx, ny, mapId, head);

							if (petNpc instanceof L1SummonInstance) { // サモンモンスター
								final L1SummonInstance summon = (L1SummonInstance) petNpc;
								pc.sendPackets(new S_SummonPack(summon, pc));
							} else if (petNpc instanceof L1PetInstance) { // ペット
								final L1PetInstance pet = (L1PetInstance) petNpc;
								pc.sendPackets(new S_PetPack(pet, pc));
							}

							for (final L1PcInstance visiblePc : L1World
									.getInstance().getVisiblePlayer(petNpc)) {
								// テレポート元と先に同じPCが居た場合、正しく更新されない為、一度removeする。
								visiblePc.removeKnownObject(petNpc);
								subjects.add(visiblePc);
							}
						}
					}
					// マジックドールも一緒に移動させる。
					final Collection<L1DollInstance> _removeList = pc.getDollList().getAllDolls();
					for (final L1DollInstance doll : _removeList) {
						if (doll.isDead()) {
							continue;
						}
						// テレポート先の設定
						final L1Location loc = pc.getLocation().randomLocation(3,
								false);
						int nx = loc.getX();
						int ny = loc.getY();
						// opqlo 賭肥區判斷
						if (L1CastleLocation.checkInAllfatArea(nx, ny, mapId)) {
							nx = pc.getX();
							ny = pc.getY();
						}
						// opqlo 賭肥區判斷
						teleport(doll, nx, ny, mapId, head);
						pc.sendPackets(new S_DollPack(doll, pc));

						for (final L1PcInstance visiblePc : L1World.getInstance()
								.getVisiblePlayer(doll)) {
							// テレポート元と先に同じPCが居た場合、正しく更新されない為、一度removeする。
							visiblePc.removeKnownObject(doll);
							subjects.add(visiblePc);
						}
					}
				}

				for (final L1PcInstance updatePc : subjects) {
					updatePc.updateObject();
				}
				subjects.clear();
				pc.setTeleport(false);
				if (pc.hasSkillEffect(WIND_SHACKLE)) {
					pc.sendPackets(new S_SkillIconWindShackle(pc.getId(), pc.getSkillEffectTimeSec(WIND_SHACKLE)));
				}
				//			pc.sendPackets(new S_Paralysis(S_Paralysis.TYPE_TELEPORT_UNLOCK, true));
				if(pc.hasSkillEffect(PC_IsPinkName)){
					pc.sendPackets(new S_PinkName(pc.getId() , pc.getSkillEffectTimeSec(PC_IsPinkName)));
				}
		} catch (final Exception e) {
			_log.log(Level.SEVERE, e.getLocalizedMessage(), e);
		}
	}

	/**
	 * 寵物的傳送
	 * 
	 * @param npc
	 * @param x
	 * @param y
	 * @param map
	 * @param head
	 */
	private static void teleport(final L1NpcInstance npc, final int x,
			final int y, final short map, final int head) {
		try {
			L1World.getInstance().moveVisibleObject(npc, map);
			if (npc instanceof L1DollInstance == false) {
				L1WorldMap.getInstance().getMap(npc.getMapId())
				.setPassable(npc.getX(), npc.getY(), true);
			}
			npc.setX(x);
			npc.setY(y);
			npc.setMap(map);
			npc.setHeading(head);
			if (npc instanceof L1DollInstance == false) {
				L1WorldMap.getInstance().getMap(npc.getMapId())
				.setPassable(npc.getX(), npc.getY(), false);
			}
		} catch (final Exception e) {
			_log.log(Level.SEVERE, e.getLocalizedMessage(), e);
		}
	}

}
